Interactive ggplots: a brief introduction

An exercise in plotly-ing

Author
Affiliation

Cecil Philip John

Georgetown University

Supercharging ggplot2

We start with the question, what if ggplot had wings? ggplot2 is the de-facto swiss army knife of creating visualizations in R. Laced with the tidyverse, its usefulness has always expanded. Being familiar with the library has put it to use beyond the generation of static, flat images. Here, I seek to set a brief primer of how to extend the powers of ggplot so that it can easily adapt to the interactive screens its graphics are taken to.

Interactivity and graphics

There is a developed and now natural intuition to hover above charts and graphs that we see on the screens we interact with, with an expectation of some response. With the possibilities of sharing ideas born in R through markdown documents, shiny dashboards or HTML formats, the shining armor of ggplot2 loses its sheen in only one-dimension - interactivity, in these mediums. The publication-quality graphics produced through ggplot2 do not necessarily create memorable experiences for users to “interact” with it but instead feel “flat”. While there are several ways to create visualizations specifically for these use cases, there exist several “low-code” means by which plots generated by ggplot can be made interactive and its utility extended.

plotly

Plotly offers a whole suite of graphing libraries across Python, Javascript and R, among others. It helps create interactive, web-based graphics. The plotly R package is a simple, easy to implement way to add interactivity to ggplots. It is based on the JavaScript graphing library plotly.js. In the words of its developers, the package,

“…serializes ggplot2 figures into Plotly’s universal graph JSON. plotly::ggplotly will crawl the ggplot2 figure, extract and translate all of the attributes of the ggplot2 figure into JSON (the colors, the axes, the chart type, etc), and draw the graph with plotly.js.”

- https://plotly.com/r/getting-started/

Plotly in R can be accessed through its plot_ly and ggplotly arguments. The greatest benefit of ggplotly is that it can be used with ggplot which ensures that the core visualization is one that can be created using familiar ggplot. In this overview, we restrict our scope to only discussing ggplotly.

Features

Plots produced through ggplotly provide opportunities to:

  • Hover over the chart and view labels associated with the data points.

  • Zoom, pan and focus on specific areas of the plot.

  • Download a copy of the plot.

At a glance

Quick view of the possibilities that are discussed on this page.

Code
# Creating a plot of the status of gdp, lifeexp and pop in 2007

lifeexpgdpplot <- 
  gapminder %>% 
  filter(year == 2007) %>% 
  ggplot(aes(
    x = gdpPercap, 
    y = lifeExp,
    size = pop,
    color = continent)) +
  geom_point() +
  labs(
    title = "GDP and Lifeexpectancy of countries in 2007") +
  theme_light() +
 paletteer::scale_colour_paletteer_d("colRoz::i_lesueurii")

# Calling the ggplot obj

lifeexpgdpplot

Code
# Calling the ggplot obj using ggplotly

ggplotly(lifeexpgdpplot)
Code
# Creating a ggplot object with additional attributes for animation

lifeexpgdpanim <- 
  gapminder %>% 
  ggplot(aes(
    x = gdpPercap,
    y = lifeExp,
    color = continent)) +
  geom_point(aes(
    size = pop,
    ids = country, 
    frame = year)) +
  theme_light() +
  paletteer::scale_colour_paletteer_d("PNWColors::Bay")

# Calling the ggplot obj using ggplotly

ggplotly(lifeexpgdpanim)

Now that we know about what is possible, let’s get down to work.

Installing plotly

Installing is straightforward. The plotly package can be accessed via CRAN.

install.packages("plotly")

Run ggplotly

We can explore the utility of the package by plotting some charts from the gapminder dataset.

Code
# Loading the required libraries

library(gapminder)
library(plotly)
library(tidyverse)

# Creating a plot using the gapminder dataset

gapminder %>% 
  filter(year == 2007) %>% 
  ggplot(aes(
    x = continent, 
    y = lifeExp)) +
  geom_boxplot(color = "#42047e") +
  geom_jitter(color = "#f1515e", 
              alpha = 0.7) +
  theme_classic() 

Plain ggplot, When we hover around the plot, we can see… that nothing happens!

When we hover around the plot, we can see… that nothing happens!

The primary requirement of using ggploty from the ploty package is to have a ggplot object.(This also means that ggplotly ) In our case we have created lifeexpplot as a ggplot object. We then call this ggplot object using ggplotly.

Code
lifeexpplot <- 
  gapminder %>% 
  filter(year == 2007) %>% 
  ggplot(aes(
    x = continent, 
    y = lifeExp)) +
  geom_boxplot(color = "#42047e") +
  geom_jitter(color = "#f1515e", 
              alpha = 0.7) +
  theme_classic() 

print(paste0("The lifeexpplot is of the class: ", class(lifeexpplot)))
[1] "The lifeexpplot is of the class: gg"    
[2] "The lifeexpplot is of the class: ggplot"

Code
ggplotly(lifeexpplot)

Interactive ggplotly

Caution

Interactivity has entered the screen. Interactivity can get contagious! Tread with caution (or spend hours hovering around scouting for the best labels and tool tips).

Interactivity yields information

We can see that enhancement has made the plot immediately more appealing by being able to provide more information, interactively, on-demand. By being able to show certain information in this interactive manner, we are able to maintain the aesthetics of the original ggplot without overcrowding it with labels and annotations.

The ModeBar

Buttons associated with the Plotly Mode Bar

Aside from the labels, an immediate change that we can see to the plot is that a plotly ribbon, or modebar, is added. Each of these buttons bring additional functionalities to the plot. Handy among these are options to download the plot, and zoom-in and out.

Modifying elements

Without any specific customization, the labels and the ribbon may be overwhelming. It is possible to modify these elements to reflect our use-cases.

config()

It is possible to remove the modebar completely, drop certain buttons or add additional custom buttons using config(). Setting config(displayModeBar = FALSE) removes the modebar entirely. We can pass a list using modeBarButtonsToRemove if there are specific buttons that we want removed from the modebar. The current list of the names of the modebar buttons can be accessed via this link.

tooltip

The default behavior of plotly is to include all the aesthetic mappings from the ggplot layers to the tooltips. In the need of drawing attention to only particular values, the tooltips can be customized to restrict what is shown. A text aesthetic defined in ggplot can be used to create a specific label that can then be accessed via tooltip argument of plotly.

Code
modplot <- 
  gapminder %>% 
  filter(year == 2007) %>% 
  ggplot(aes(
    x = continent, 
    y = lifeExp,
    text = country)) +
  geom_boxplot(color = "#FFA400FF") +
  geom_jitter(color = "#088158FF", 
              alpha = 0.7) +
  theme_classic() 

ggplotly(modplot, tooltip = 'text') %>% 
  config(displayModeBar = FALSE)

Here we set the labels as the names of the countries and remove the modebar.

Animation

Code
# Plotting 

pop_plot <- 
gapminder %>% 
  filter(country %in% 
           (gapminder %>% 
              filter(year == 2007) %>% 
              slice_max(pop, n = 5) %>% 
              pull(country))) %>% 
  ggplot(aes(x = year, 
    y = pop, 
    color = country)) +
  geom_line(
    alpha = 0.4, 
    linetype = 'dashed') +
  geom_point(aes(
    size = gdpPercap, 
    ids = country, 
    frame = year))+
  theme_classic() +
  paletteer::scale_colour_paletteer_d("fishualize::Antennarius_commerson")

ggplotly(pop_plot)

In use-cases where it can help show to changes, over a period of time for instance, adding motion-graphics might be useful. The plot shown above has the added element of a Play button, which can be used to auto-transition between values across the years.

We are able to introduce this interaction using the frame and ids arguments, but primarily through frame. The value that has to be attached to frame would be the one that necessitates the new observation, year in our example. ids represents the object being re-observed and is declared for ensuring smoother transitions between frames.

Tip

There are other ways to produce animated plots in R and produce similar results. gganimate is a package that works on this functionality.

Linked plots

Code
# Limiting the dataset to only values from 2007

gapminder_2007 <- 
  gapminder %>% 
  filter(year == 2007)


# Creating a shared data frame

gap_key <- highlight_key(gapminder_2007, ~continent)


# Plot 1

continent_plot <- 
  ggplot(gap_key, 
         aes(x = lifeExp, 
             fill = continent)) +
  geom_density(alpha = 0.6) +
  paletteer::scale_fill_paletteer_d("lisa::C_M_Coolidge") +
  xlab("continents") +
  theme_classic()


# Plot 2

country_plot <- 
  ggplot(gap_key, 
         aes(x = gdpPercap,
             y = lifeExp, 
             size = pop,
             fill = continent)) +
  geom_point() +
  theme_classic() +
  paletteer::scale_fill_paletteer_d("lisa::C_M_Coolidge")


# Plotting them together

subplot(continent_plot, country_plot) %>% 
  hide_legend() %>%
  highlight("plotly_hover")

We can use these visualizations when we have linked graphics, where it is helpful to show relevant points on a second plot, based on values selected on another plot. In this example, we are able to isolate the values that belong to countries of a continent, based on the selection of the continent on the first plot.

A shared data frame is created that declares the data frame and the connecting variable that is used between the plots. It is this object that is used to as the data layer for creating the ggplots. When passed through the subplot argument, we are able to create plotly versions with both the plots placed side-by-side.

Tip

There are other ways to create linked plots in R and produce similar results. ggiraph is a package produces similar functionality.

References